import { BaseGame, GameStates, GAME_PAD_MATRIX_H, GAME_PAD_MATRIX_W, _LEVEL_MAX, _LEVEL_MIN, _REST_LINE_DURATION, _SPEED } from '../BaseGame';


const enum MoveDirection {
  up,
  right,
  down,
  left
}

export class SnakeGame extends BaseGame {
  _snake: Array<Array<number>> = Array<Array<number>>();
  _food: Array<number> = Array<number>();
  _points: number = 0;
  _cleared: number = 0;
  _states: GameStates = GameStates.none;
  _currentDirection = MoveDirection.right;

  _autoMoveTimer;

  constructor() {
    super();
    this.clickConfirm()
  }


  reset() {
    if (this._states === GameStates.none) {
      // 可以开始游戏
      this._startGame();
      return;
    }
    if (this._states === GameStates.reset) {
      return;
    }
    // this._sound.start();
    this._states = GameStates.reset;
    this._currentDirection = MoveDirection.right;
    (async () => {
      let line = GAME_PAD_MATRIX_H;
      await new Promise<void>(async (resolve) => {
        while (line > 0) {
          line--;
          for (let i = 0; i < GAME_PAD_MATRIX_W; i++) {
            this._data[line][i] = 1;
          }
          this._buildMatrix()
          await new Promise(resolve => setTimeout(resolve, _REST_LINE_DURATION));
        }
        resolve();
      });
      this._points = 0;
      this._cleared = 0;
      await new Promise<void>(async (resolve) => {
        while (line < GAME_PAD_MATRIX_H) {
          for (let i = 0; i < GAME_PAD_MATRIX_W; i++) {
            this._data[line][i] = 0;
            this._mask[line][i] = 0;
          }
          this._buildMatrix()
          line++;
          await new Promise(resolve => setTimeout(resolve, _REST_LINE_DURATION));
        }
        resolve();
      });
      this._resetSnakeFood()
      this._buildMatrix()
      this._states = GameStates.none
    })();
  }

  startOrPause() {
    if (this._states == GameStates.running) {
      this.pause();
    } else if (this._states == GameStates.paused || this._states == GameStates.none) {
      this.start();
    }
  }

  start() {
    this._startGame()
  }

  pause() {
    if (this._states == GameStates.running) {
      this._states = GameStates.paused;
    }
    this._buildMatrix()
  }

  clickD() {
    if (this._states === GameStates.running) {
      if (this._currentDirection == MoveDirection.up) return;
      this._currentDirection = MoveDirection.down;
      this._move()
    }
  }

  clickU() {
    if (this._states === GameStates.running) {
      if (this._currentDirection == MoveDirection.down) return;
      this._currentDirection = MoveDirection.up;
      this._move()
    }
  }

  clickR() {
    if (this._states === GameStates.none && this._level < _LEVEL_MAX) {
      this._level++;
    } else if (this._states === GameStates.running) {
      if (this._currentDirection == MoveDirection.left) return;
      this._currentDirection = MoveDirection.right;
      this._move()
    }
  }

  clickL() {
    if (this._states === GameStates.none && this._level > _LEVEL_MIN) {
      this._level--;
    } else if (this._states === GameStates.running) {
      if (this._currentDirection == MoveDirection.right) return;
      this._currentDirection = MoveDirection.left;
      this._move()
    }
  }


  async clickConfirm() {
    if (this._states === GameStates.running) {
      this._move()
    } else if (this._states === GameStates.paused || this._states === GameStates.none) {
      this._resetSnakeFood()
      this._buildMatrix()
    }
  }

  private _resetSnakeFood() {
    this._food = this._randomFood();
    this._snake = [
      [0, 0],
      [1, 0],
      [2, 0]
    ];
    this._buildMatrix()
  }

  private _randomFood(rowIndex?: number): number[] {
    let y: number;
    if (rowIndex === undefined) {
      y = Math.floor(Math.random() * GAME_PAD_MATRIX_H);
      if (!this._data[y].includes(0)) {
        return this._randomFood();
      }
    } else {
      y = rowIndex;
    }
    const x: number = Math.floor(Math.random() * GAME_PAD_MATRIX_W);
    if (this._data[y][x] !== 0) {
      // 位置已被占用。重新获取
      return this._randomFood(rowIndex);
    }
    return [x, y];
  }

  private _isFull(rowIndex?: number): boolean {
    if (rowIndex === undefined) {
      // 判断整体
      for (let i = 0; i < this._data.length; i++) {
        if (this._data[i].includes(0)) {
          return false;
        }
      }
    } else {
      // 判断单行
      if (rowIndex < 0 || rowIndex >= this._data.length) {
        return true;
      }
      if (this._data[rowIndex].includes(0)) {
        return false;
      }
    }
    return true;
  }

  private _autoMove(enable: boolean): void {
    if (!enable) {
      clearInterval(this._autoMoveTimer)
      this._autoMoveTimer = null;
    } else if (enable) {
      clearInterval(this._autoMoveTimer)
      this._food = this._food || this._randomFood();
      this._autoMoveTimer = setInterval(() => {
        this._move()
      }, _SPEED[this._level - 1]);
    }
  }

  async _move(): Promise<void> {
    if (this._states === GameStates.running) {
      // 增加头
      const head = this._snake[this._snake.length - 1];
      let newHead: number[];
      switch (this._currentDirection) {
        case MoveDirection.right:
          newHead = [head[0] + 1, head[1]];
          break;
        case MoveDirection.left:
          newHead = [head[0] - 1, head[1]];
          break;
        case MoveDirection.up:
          newHead = [head[0], head[1] - 1];
          break;
        case MoveDirection.down:
          newHead = [head[0], head[1] + 1];
          break;
        default:
          break;
      }
      const status = this._getHeadStatus(newHead);
      this._snake.push(newHead);
      this._buildMatrix()

      switch (status) {
        case -1:
        // 碰壁死亡。闪烁之后重置
        //   this.sound.clear();
        // 取消当前下落
          this._autoMove(false);
        // 消除效果动画。闪烁5次
          for (let count = 0; count < 6; count++) {
            // 去掉蛇头闪烁
            for (let i = 0; i < this._snake.length; i++) {
              if (
                this._snake[i][0] < 0 ||
                this._snake[i][1] < 0 ||
                this._snake[i][0] >= GAME_PAD_MATRIX_W ||
                this._snake[i][1] >= GAME_PAD_MATRIX_H
              ) {
                continue;
              }
              this._mask[this._snake[i][1]][this._snake[i][0]] =
                count % 2 === 0 ? -1 : 1;
            }
            this._buildMatrix()
            if (count < 6) {
              await new Promise(resolve => setTimeout(resolve, 100));
            }
          }
          this.reset();
          break;
        case 1:
        // 吃掉食物
        //   this.sound.rotate();
          this._cleared++;
          this._points += this._level * 5;
          if (this._isFull()) {
            this.reset();
            return;
          }
          this._food = this._randomFood();
          this._buildMatrix()
          break;
        case 0:
        // 继续前行。 砍掉尾巴
        //   if (enableSounds) {
        //     this.sound.move();
        //   }
          this._snake.shift();
          break;
        default:
      }
    }
    this._buildMatrix()
  }

  // 获取蛇头的情况
  //   -1 碰壁死亡
  //    0 继续往前
  //    1 吃掉食物
  private _getHeadStatus(point: number[]): number {
    if (!point || point.length < 2) {
      return -1;
    }
    // 越界
    if (
      point[0] < 0 ||
      point[0] >= GAME_PAD_MATRIX_W ||
      point[1] < 0 ||
      point[1] >= GAME_PAD_MATRIX_H
    ) {
      return -1;
    }
    // 碰到食物
    if (point.toString() === this._food.toString()) {
      return 1;
    }
    // 吃掉自己
    for (const item of this._snake) {
      if (point.toString() === item.toString()) {
        return -1;
      }
    }
    return 0;
  }

  private _startGame(): void {
    if (this._states === GameStates.running && this._autoMoveTimer && !this._autoMoveTimer.isActive) {
      return;
    }
    this._states = GameStates.running;
    this._buildMatrix()
    this._autoMove(true);
  }

  private _buildMatrix(): void {
    var mixed: number[][] = [];

    for (let i = 0; i < GAME_PAD_MATRIX_H; i++) {
      mixed.push(Array(GAME_PAD_MATRIX_W).fill(0));

      for (let j = 0; j < GAME_PAD_MATRIX_W; j++) {
        let value = this._data[i][j];

        if (this._food && this._food.length === 2 && this._food[0] === j && this._food[1] === i) {
          value = 2;
        }

        if (this._snake && this._snake.length > 0) {
          for (let k = 0; k < this._snake.length; k++) {
            if (this._snake[k][0] === j && this._snake[k][1] === i) {
              if (k === this._snake.length - 1) {
                // 蛇头
                value = 2;
              } else {
                // 蛇身
                value = 1;
              }
              continue;
            }
          }
        }

        if (this._mask[i][j] === -1) {
          value = 0;
        } else if (this._mask[i][j] === 1) {
          value = 2;
        }
        mixed[i][j] = value;
      }
    }
    this._mixed = mixed
  }
}